<!DOCTYPE html>


<html lang="zh-CN">
  

    <head>
      <meta charset="utf-8" />
       
      <meta name="keywords" content="陈亚东 cyd 凤舞神者" />
       
      <meta name="description" content="85年的精神小伙,不是神者有点神经,水尿吧汤 " />
      
      <meta
        name="viewport"
        content="width=device-width, initial-scale=1, maximum-scale=1"
      />
      <title>springboot整合mybatis源码分析 |  风舞神者</title>
  <meta name="generator" content="hexo-theme-ayer">
      
      <link rel="shortcut icon" href="/favicon.ico" />
       
<link rel="stylesheet" href="/dist/main.css">

      
<link rel="stylesheet" href="/css/fonts/remixicon.css">

      
<link rel="stylesheet" href="/css/custom.css">
 
      <script src="https://cdn.staticfile.org/pace/1.2.4/pace.min.js"></script>
       
 

      <link
        rel="stylesheet"
        href="https://cdn.jsdelivr.net/npm/@sweetalert2/theme-bulma@5.0.1/bulma.min.css"
      />
      <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11.0.19/dist/sweetalert2.min.js"></script>

      <!-- mermaid -->
      
      <style>
        .swal2-styled.swal2-confirm {
          font-size: 1.6rem;
        }
      </style>
    </head>
  </html>
</html>


<body>
  <div id="app">
    
      
    <main class="content on">
      <section class="outer">
  <article
  id="post-springboot整合mybatis源码分析"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h1 class="article-title sea-center" style="border-left:0" itemprop="name">
  springboot整合mybatis源码分析
</h1>
 

      
    </header>
     
    <div class="article-meta">
      <a href="/2023/07/06/springboot%E6%95%B4%E5%90%88mybatis%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/" class="article-date">
  <time datetime="2023-07-06T06:42:18.000Z" itemprop="datePublished">2023-07-06</time>
</a>   
<div class="word_count">
    <span class="post-time">
        <span class="post-meta-item-icon">
            <i class="ri-quill-pen-line"></i>
            <span class="post-meta-item-text"> 字数统计:</span>
            <span class="post-count">6k</span>
        </span>
    </span>

    <span class="post-time">
        &nbsp; | &nbsp;
        <span class="post-meta-item-icon">
            <i class="ri-book-open-line"></i>
            <span class="post-meta-item-text"> 阅读时长≈</span>
            <span class="post-count">27 分钟</span>
        </span>
    </span>
</div>
 
    </div>
      
    <div class="tocbot"></div>




  
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="springboot整合mybatis源码分析-转https-www-cnblogs-com-wbo112-p-15025348-html"><a href="#springboot整合mybatis源码分析-转https-www-cnblogs-com-wbo112-p-15025348-html" class="headerlink" title="springboot整合mybatis源码分析- 转https://www.cnblogs.com/wbo112/p/15025348.html"></a>springboot整合mybatis源码分析- 转<a target="_blank" rel="noopener" href="https://www.cnblogs.com/wbo112/p/15025348.html">https://www.cnblogs.com/wbo112/p/15025348.html</a></h1><p>本文主要讲述mybatis在springboot中是如何被加载执行的，由于涉及的内容会比较多，所以这次只会对调用关系及关键代码点进行讲解，为了避免文章太长，读起来昏昏欲睡，一些不影响整体流程的细节就不涉及了。</p>
<p>源码位置<a target="_blank" rel="noopener" href="https://github.com/wbo112/blogdemo/tree/main/springbootdemo/springboot-mybatis">https://github.com/wbo112/blogdemo/tree/main/springbootdemo/springboot-mybatis</a></p>
<h3 id="1、预备知识"><a href="#1、预备知识" class="headerlink" title="1、预备知识"></a>1、预备知识</h3><ul>
<li><p>FactoryBean</p>
<p>什么是FactoryBean？</p>
<p>我们先看看FactoryBean的源码</p>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//由 BeanFactory 中使用的对象实现的接口，这些对象本身是单个对象的工厂。如果一个 bean 实现了这个接口，它就被用作一个对象暴露的工厂，而不是直接作为一个将暴露自己的 bean 实例。</span></span><br><span class="line"><span class="comment">//注意：实现此接口的 bean 不能用作普通 bean。 FactoryBean 以 bean 样式定义，但为 bean 引用公开的对象 (getObject()) 始终是它创建的对象。</span></span><br><span class="line"><span class="comment">//FactoryBeans 可以支持单例和原型，并且可以根据需要懒惰地或在启动时急切地创建对象。 SmartFactoryBean 接口允许公开更细粒度的行为元数据。</span></span><br><span class="line"><span class="comment">//该接口在框架本身中被大量使用，例如用于 AOP org.springframework.aop.framework.ProxyFactoryBean 或 org.springframework.jndi.JndiObjectFactoryBean。它也可以用于自定义组件；然而，这仅适用于基础设施代码。</span></span><br><span class="line"><span class="comment">//FactoryBean 是一个程序化契约。实现不应该依赖于注释驱动的注入或其他反射设施。 getObjectType() getObject() 调用可能会在引导过程的早期到达，甚至在任何后处理器设置之前。如果您需要访问其他 bean，请实现 BeanFactoryAware 并以编程方式获取它们。</span></span><br><span class="line"><span class="comment">//容器只负责管理FactoryBean 实例的生命周期，而不负责管理FactoryBean 创建的对象的生命周期。因此，暴露的 bean 对象（例如 java.io.Closeable.close() 上的 destroy 方法不会被自动调用。相反，FactoryBean 应该实现 DisposableBean 并将任何此类关闭调用委托给底层对象。</span></span><br><span class="line"><span class="comment">//最后，FactoryBean 对象参与包含 BeanFactory 的 bean 创建同步。除了 FactoryBean 本身（或类似的）内部的延迟初始化之外，通常不需要内部同步。</span></span><br><span class="line"><span class="keyword">package</span> org.springframework.beans.factory;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.lang.Nullable;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">FactoryBean</span>&lt;T&gt; &#123;</span><br><span class="line"></span><br><span class="line">	<span class="type">String</span> <span class="variable">OBJECT_TYPE_ATTRIBUTE</span> <span class="operator">=</span> <span class="string">&quot;factoryBeanObjectType&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//返回真正的beanFacotry中的bean对象</span></span><br><span class="line">	<span class="meta">@Nullable</span></span><br><span class="line">	T <span class="title function_">getObject</span><span class="params">()</span> <span class="keyword">throws</span> Exception;</span><br><span class="line">    </span><br><span class="line">	<span class="comment">//返回真正的beanFacotry中的bean对象的类型</span></span><br><span class="line">	<span class="meta">@Nullable</span></span><br><span class="line">	Class&lt;?&gt; getObjectType();</span><br><span class="line">    </span><br><span class="line">	<span class="comment">//是否单例</span></span><br><span class="line">	<span class="keyword">default</span> <span class="type">boolean</span> <span class="title function_">isSingleton</span><span class="params">()</span> &#123;</span><br><span class="line">		<span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面就是FactoryBean的源码了，源码中的注释我都删除掉了。类上的中文注释是翻译的源码上的，方法上的注释是我自己加的。简单来说就是时间这个接口的类是作为对象暴漏的工厂，真正调用getObject()才会得到实际的bean对象。</p>
<h3 id="2、springboot集成mybatis"><a href="#2、springboot集成mybatis" class="headerlink" title="2、springboot集成mybatis"></a>2、springboot集成mybatis</h3><ul>
<li><p>之前的文章简单说到springboot启动的时候会读取META-INF\spring.factories文件，把key&#x3D;org.springframework.boot.autoconfigure.EnableAutoConfiguration的字符串作为类名去加载(启动会配合META-INF\spring-autoconfigure-metadata.properties中的内容过滤掉不符合当前场景的)</p>
<p>springboot集成mybatis也是这样实现的。</p>
</li>
</ul>
<p><img src="https://img2020.cnblogs.com/blog/368308/202107/368308-20210717224425807-1892423111.png" alt="img"></p>
<ul>
<li><p>是由谁来上面的文件的呢</p>
<p>我们的main方法上都会有@SpringBootApplication注解</p>
</li>
</ul>
<p><img src="https://cydmacro.gitee.io/myblog/img/368308-20210717224440261-115567715.png" alt="img"></p>
<p>在SpringBootApplication这个上面会有个@EnableAutoConfiguration注解</p>
<p><img src="https://img2020.cnblogs.com/blog/368308/202107/368308-20210717224449523-1318992810.png" alt="img"></p>
<p>在这个上面会有import注解，参数是AutoConfigurationImportSelector.class。真正读取上面文件的类就是AutoConfigurationImportSelector。</p>
<p><img src="https://img2020.cnblogs.com/blog/368308/202107/368308-20210717224457195-1778508685.png" alt="img"></p>
<p>AutoConfigurationImportSelector.java</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//真正的读取代码是在这里</span></span><br><span class="line">	<span class="keyword">protected</span> AutoConfigurationEntry <span class="title function_">getAutoConfigurationEntry</span><span class="params">(AnnotationMetadata annotationMetadata)</span> &#123;</span><br><span class="line">		<span class="keyword">if</span> (!isEnabled(annotationMetadata)) &#123;</span><br><span class="line">			<span class="keyword">return</span> EMPTY_ENTRY;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="type">AnnotationAttributes</span> <span class="variable">attributes</span> <span class="operator">=</span> getAttributes(annotationMetadata);</span><br><span class="line">		<span class="comment">//在这里读取META-INF\spring.factories文件中key=org.springframework.boot.autoconfigure.EnableAutoConfiguration的值</span></span><br><span class="line">		List&lt;String&gt; configurations = getCandidateConfigurations(annotationMetadata, attributes);</span><br><span class="line">		configurations = removeDuplicates(configurations);</span><br><span class="line">		Set&lt;String&gt; exclusions = getExclusions(annotationMetadata, attributes);</span><br><span class="line">		checkExcludedClasses(configurations, exclusions);</span><br><span class="line">		configurations.removeAll(exclusions);</span><br><span class="line">        <span class="comment">//在这里读取META-INF\spring.factories文件中key=org.springframework.boot.autoconfigure.AutoConfigurationImportFilter的值根据META-INF\spring-autoconfigure-metadata.properties进行过滤</span></span><br><span class="line">		configurations = getConfigurationClassFilter().filter(configurations);</span><br><span class="line">		fireAutoConfigurationImportEvents(configurations, exclusions);</span><br><span class="line">		<span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">AutoConfigurationEntry</span>(configurations, exclusions);</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>

<p>读取META-INF\spring-autoconfigure-metadata.properties文件是在AutoConfigurationImportSelector的内部类ConfigurationClassFilter的构造方法中，真正的过滤也是在这个内部类中</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">		ConfigurationClassFilter(ClassLoader classLoader, List&lt;AutoConfigurationImportFilter&gt; filters) &#123;</span><br><span class="line">		<span class="comment">//在这里读取的META-INF\spring-autoconfigure-metadata.properties</span></span><br><span class="line">			<span class="built_in">this</span>.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(classLoader);</span><br><span class="line">			<span class="built_in">this</span>.filters = filters;</span><br><span class="line">		&#125;</span><br><span class="line"><span class="comment">//这个也是ConfigurationClassFilter的方法</span></span><br><span class="line">List&lt;String&gt; <span class="title function_">filter</span><span class="params">(List&lt;String&gt; configurations)</span> &#123;</span><br><span class="line">   <span class="type">long</span> <span class="variable">startTime</span> <span class="operator">=</span> System.nanoTime();</span><br><span class="line">   String[] candidates = StringUtils.toStringArray(configurations);</span><br><span class="line">   <span class="type">boolean</span> <span class="variable">skipped</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line">   <span class="keyword">for</span> (AutoConfigurationImportFilter filter : <span class="built_in">this</span>.filters) &#123;</span><br><span class="line">   	<span class="comment">//执行过滤</span></span><br><span class="line">      <span class="type">boolean</span>[] match = filter.match(candidates, <span class="built_in">this</span>.autoConfigurationMetadata);</span><br><span class="line">      <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; match.length; i++) &#123;</span><br><span class="line">         <span class="keyword">if</span> (!match[i]) &#123;</span><br><span class="line">            candidates[i] = <span class="literal">null</span>;</span><br><span class="line">            skipped = <span class="literal">true</span>;</span><br><span class="line">         &#125;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">     + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + <span class="string">&quot; ms&quot;</span>);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>默认的过滤器是有3个，是在这里</p>
<p><img src="https://img2020.cnblogs.com/blog/368308/202107/368308-20210717224513000-1782254253.png" alt="img"></p>
<p>在读取过程中就会读取mybatis-spring-boot-autoconfigure-2.2.0.jar中的META-INF\spring.factories配置(本文第一个图)，加载下面两个类</p>
<figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">org.springframework.boot.autoconfigure.EnableAutoConfiguration</span>=<span class="string">\</span></span><br><span class="line"><span class="string">org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\</span></span><br><span class="line"><span class="string">org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration</span></span><br></pre></td></tr></table></figure>

<p>同样的也会用mybatis-spring-boot-autoconfigure-2.2.0.jar中的META-INF\spring-autoconfigure-metadata.properties文件进行过滤。</p>
<p><img src="https://img2020.cnblogs.com/blog/368308/202107/368308-20210717224524837-1746615215.png" alt="img"></p>
<p>这里的过滤其实就是用类名+.+Conditional*来作为过滤的</p>
<figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration.ConditionalOnClass</span>=<span class="string">org.apache.ibatis.session.SqlSessionFactory,org.mybatis.spring.SqlSessionFactoryBean</span></span><br><span class="line"><span class="attr">org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration.ConditionalOnSingleCandidate</span>=<span class="string">javax.sql.DataSource</span></span><br></pre></td></tr></table></figure>

<p>比如上面两行org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration.ConditionalOnClass是根据等号后面的类是否存在来判断是否被过滤掉，org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration.ConditionalOnSingleCandidate看代码也是判断对应类是否存在来判断的，多个条件是and的关系</p>
<p>这两个条件的具体判断代码位置在OnBeanCondition中</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">protected</span> <span class="keyword">final</span> ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,</span><br><span class="line">      AutoConfigurationMetadata autoConfigurationMetadata) &#123;</span><br><span class="line">   ConditionOutcome[] outcomes = <span class="keyword">new</span> <span class="title class_">ConditionOutcome</span>[autoConfigurationClasses.length];</span><br><span class="line">   <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; outcomes.length; i++) &#123;</span><br><span class="line">      <span class="type">String</span> <span class="variable">autoConfigurationClass</span> <span class="operator">=</span> autoConfigurationClasses[i];</span><br><span class="line">      <span class="keyword">if</span> (autoConfigurationClass != <span class="literal">null</span>) &#123;</span><br><span class="line">          <span class="comment">//获取*.ConditionalOnClass等号后面的值</span></span><br><span class="line">         Set&lt;String&gt; onBeanTypes = autoConfigurationMetadata.getSet(autoConfigurationClass, <span class="string">&quot;ConditionalOnBean&quot;</span>);</span><br><span class="line">          <span class="comment">//进行判断，返回null就是OK的，条件不存在也是null</span></span><br><span class="line">         outcomes[i] = getOutcome(onBeanTypes, ConditionalOnBean.class);</span><br><span class="line">         <span class="keyword">if</span> (outcomes[i] == <span class="literal">null</span>) &#123;</span><br><span class="line">             <span class="comment">//获取*.ConditionalOnSingleCandidate等号后面的值</span></span><br><span class="line">            Set&lt;String&gt; onSingleCandidateTypes = autoConfigurationMetadata.getSet(autoConfigurationClass,</span><br><span class="line">                  <span class="string">&quot;ConditionalOnSingleCandidate&quot;</span>);</span><br><span class="line">              <span class="comment">//进行判断，返回null就是OK的，条件不存在也是null</span></span><br><span class="line">            outcomes[i] = getOutcome(onSingleCandidateTypes, ConditionalOnSingleCandidate.class);</span><br><span class="line">         &#125;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">return</span> outcomes;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>当前的场景这两个类都是符合的不会被过滤掉。这两个类就会被加载。</p>
<h3 id="3、MybatisAutoConfiguration的加载，beanFatory加载-Mapper类"><a href="#3、MybatisAutoConfiguration的加载，beanFatory加载-Mapper类" class="headerlink" title="3、MybatisAutoConfiguration的加载，beanFatory加载@Mapper类"></a>3、MybatisAutoConfiguration的加载，beanFatory加载@Mapper类</h3><p>下面具体看下加载的过程，主要是MybatisAutoConfiguration这个类，所以我们这里也就只看这个类了</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//这里就把类上的注解粘了出来简单都介绍下</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//这个注解大家都比较熟悉，不多说了</span></span><br><span class="line"><span class="meta">@org</span>.springframework.context.annotation.Configuration</span><br><span class="line"><span class="comment">//这个还是条件注解，处理的类和上面配置文件中的处理都在同一个类中</span></span><br><span class="line"><span class="comment">//这个是判断对应类是否存在</span></span><br><span class="line"><span class="meta">@ConditionalOnClass(&#123; SqlSessionFactory.class, SqlSessionFactoryBean.class &#125;)</span></span><br><span class="line"><span class="comment">//这个就和配置文件中的处理有区别了，这个是判断beanFacotry中是否只有一个类型DataSource.class的bean的定义，或者有多个，但有一个主要的</span></span><br><span class="line"><span class="meta">@ConditionalOnSingleCandidate(DataSource.class)</span></span><br><span class="line"><span class="comment">//这个是去让注入配置文件</span></span><br><span class="line"><span class="meta">@EnableConfigurationProperties(MybatisProperties.class)</span></span><br><span class="line"><span class="comment">//这个是排序的</span></span><br><span class="line"><span class="meta">@AutoConfigureAfter(&#123; DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class &#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MybatisAutoConfiguration</span> <span class="keyword">implements</span> <span class="title class_">InitializingBean</span> &#123;</span><br><span class="line">    ......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>下面这个图是ConditionalOnClass，ConditionalOnSingleCandidate执行处理的位置，还是在OnBeanCondition这个类中</p>
<p><img src="https://img2020.cnblogs.com/blog/368308/202107/368308-20210717224542641-993599938.png" alt="img"></p>
<p>在进行完过滤判断后，确定MybatisAutoConfiguration类要加载之后，会扫描内部类和方法上，符合条件的也都会被加载，主要是找@Configuration，@Bean这两个注解。我们当前这个类中依次会加载如下内容</p>
<p><img src="https://img2020.cnblogs.com/blog/368308/202107/368308-20210717224555891-677971314.png" alt="img"></p>
<p>加载这个内部类，ConditionalOnMissingBean这个条件当前是成立的，关于条件这块都会忽略掉，不多说这块了。同时由于类上有Import注解，也就会继续加载AutoConfiguredMapperScannerRegistrar.class这个类，</p>
<p>类上有方法@Bean注解</p>
<p><img src="https://cydmacro.gitee.io/myblog/img/368308-20210717224612256-1821173732.png" alt="img"></p>
<p><img src="https://img2020.cnblogs.com/blog/368308/202107/368308-20210717224621146-1713573461.png" alt="img"></p>
<p>这两个类也会被加载</p>
<p>这里就会加载MybatisAutoConfiguration，MapperScannerRegistrarNotFoundConfiguration，AutoConfiguredMapperScannerRegistrar，SqlSessionTemplate，SqlSessionFactory这几个作为bean的定义(后面两个是方法)。</p>
<p>由于AutoConfiguredMapperScannerRegistrar实现了ImportBeanDefinitionRegistrar接口，在加载的过程中，会调用registerBeanDefinitions去注册额外的bean的定义。</p>
<p>这个方法比较重要，我们进去看看</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">   <span class="meta">@Override</span></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">registerBeanDefinitions</span><span class="params">(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)</span> &#123;</span><br><span class="line"><span class="comment">//这个是判断beanFactory中是否存在AutoConfigurationPackages的bean，这里是存在的</span></span><br><span class="line">     <span class="keyword">if</span> (!AutoConfigurationPackages.has(<span class="built_in">this</span>.beanFactory)) &#123;</span><br><span class="line">       logger.debug(<span class="string">&quot;Could not determine auto-configuration package, automatic mapper scanning disabled.&quot;</span>);</span><br><span class="line">       <span class="keyword">return</span>;</span><br><span class="line">     &#125;</span><br><span class="line"></span><br><span class="line">     logger.debug(<span class="string">&quot;Searching for mappers annotated with @Mapper&quot;</span>);</span><br><span class="line">  <span class="comment">//这里获取要扫描的包名，这里会是&#123;“com.example.springbootmybatis”&#125;，其实也就是我们在哪里找mapper,后面单独说下这个</span></span><br><span class="line">     List&lt;String&gt; packages = AutoConfigurationPackages.get(<span class="built_in">this</span>.beanFactory);</span><br><span class="line">     <span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">       packages.forEach(pkg -&gt; logger.debug(<span class="string">&quot;Using auto-configuration base package &#x27;&#123;&#125;&#x27;&quot;</span>, pkg));</span><br><span class="line">     &#125;</span><br><span class="line"> <span class="comment">//下面这些代码主要就是定义一个bean的定义，添加到BeanFactory中</span></span><br><span class="line">     <span class="type">BeanDefinitionBuilder</span> <span class="variable">builder</span> <span class="operator">=</span> BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);</span><br><span class="line">     builder.addPropertyValue(<span class="string">&quot;processPropertyPlaceHolders&quot;</span>, <span class="literal">true</span>);</span><br><span class="line">     <span class="comment">//这就是要扫描的注解类型，就是@Mapper</span></span><br><span class="line">     builder.addPropertyValue(<span class="string">&quot;annotationClass&quot;</span>, Mapper.class);</span><br><span class="line">     <span class="comment">//这里是要扫描的包的路径</span></span><br><span class="line">     builder.addPropertyValue(<span class="string">&quot;basePackage&quot;</span>, StringUtils.collectionToCommaDelimitedString(packages));</span><br><span class="line">     <span class="type">BeanWrapper</span> <span class="variable">beanWrapper</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BeanWrapperImpl</span>(MapperScannerConfigurer.class);</span><br><span class="line">     Set&lt;String&gt; propertyNames = Stream.of(beanWrapper.getPropertyDescriptors()).map(PropertyDescriptor::getName)</span><br><span class="line">         .collect(Collectors.toSet());</span><br><span class="line">     <span class="keyword">if</span> (propertyNames.contains(<span class="string">&quot;lazyInitialization&quot;</span>)) &#123;</span><br><span class="line">       <span class="comment">// Need to mybatis-spring 2.0.2+</span></span><br><span class="line">       builder.addPropertyValue(<span class="string">&quot;lazyInitialization&quot;</span>, <span class="string">&quot;$&#123;mybatis.lazy-initialization:false&#125;&quot;</span>);</span><br><span class="line">     &#125;</span><br><span class="line">     <span class="keyword">if</span> (propertyNames.contains(<span class="string">&quot;defaultScope&quot;</span>)) &#123;</span><br><span class="line">       <span class="comment">// Need to mybatis-spring 2.0.6+</span></span><br><span class="line">       builder.addPropertyValue(<span class="string">&quot;defaultScope&quot;</span>, <span class="string">&quot;$&#123;mybatis.mapper-default-scope:&#125;&quot;</span>);</span><br><span class="line">     &#125;</span><br><span class="line">     registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>

<hr>
<p>这里我们来看下AutoConfigurationPackages.get(this.beanFactory)这个获取的包名是如何的</p>
<p>先看下这个方法的里面调用</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> List&lt;String&gt; <span class="title function_">get</span><span class="params">(BeanFactory beanFactory)</span> &#123;</span><br><span class="line">	<span class="keyword">try</span> &#123;</span><br><span class="line">           <span class="comment">//BEAN = AutoConfigurationPackages.class.getName(),这个方法也就是获取bean的名字是AutoConfigurationPackages.class.getName(),AutoConfigurationPackages.BasePackages.class类型的bean，再调用AutoConfigurationPackages.BasePackages的get方法</span></span><br><span class="line">           <span class="comment">//下面我们分析下这个值是怎么来的</span></span><br><span class="line">		<span class="keyword">return</span> beanFactory.getBean(BEAN, BasePackages.class).get();</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">catch</span> (NoSuchBeanDefinitionException ex) &#123;</span><br><span class="line">		<span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(<span class="string">&quot;Unable to retrieve @EnableAutoConfiguration base packages&quot;</span>);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>由于我们main方法的类上有@SpringBootApplication注解，它的注解上有@EnableAutoConfiguration，它的注解上有@AutoConfigurationPackage，它的注解上@Import(AutoConfigurationPackages.Registrar.class)，在加载我们的主类SpringbootMybatisApplication时，就会调用到AutoConfigurationPackages.Registrar的registerBeanDefinitions这个方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Registrar</span> <span class="keyword">implements</span> <span class="title class_">ImportBeanDefinitionRegistrar</span>, DeterminableImports &#123;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">       <span class="comment">//这里的metadata就是我们的SpringbootMybatisApplication</span></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">registerBeanDefinitions</span><span class="params">(AnnotationMetadata metadata, BeanDefinitionRegistry registry)</span> &#123;</span><br><span class="line">          	<span class="comment">//我们先看看new PackageImports(metadata)这个方法</span></span><br><span class="line">		register(registry, <span class="keyword">new</span> <span class="title class_">PackageImports</span>(metadata).getPackageNames().toArray(<span class="keyword">new</span> <span class="title class_">String</span>[<span class="number">0</span>]));</span><br><span class="line">	&#125;</span><br><span class="line">......</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line">	<span class="comment">//这个比较简单了，就是获取一些包名</span></span><br><span class="line">	PackageImports(AnnotationMetadata metadata) &#123;</span><br><span class="line">           </span><br><span class="line">		<span class="type">AnnotationAttributes</span> <span class="variable">attributes</span> <span class="operator">=</span> AnnotationAttributes</span><br><span class="line">				.fromMap(metadata.getAnnotationAttributes(AutoConfigurationPackage.class.getName(), <span class="literal">false</span>));</span><br><span class="line">           <span class="comment">//这里是获取我们主类SpringbootMybatisApplication上注解的&quot;basePackages&quot;属性，由于我们没有配置，所以这里就是null</span></span><br><span class="line">		List&lt;String&gt; packageNames = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(Arrays.asList(attributes.getStringArray(<span class="string">&quot;basePackages&quot;</span>)));</span><br><span class="line">             <span class="comment">//这里是获取我们主类SpringbootMybatisApplication上注解的&quot;basePackageClasses&quot;属性，由于我们没有配置，所以也不会走到这个for循环</span></span><br><span class="line">		<span class="keyword">for</span> (Class&lt;?&gt; basePackageClass : attributes.getClassArray(<span class="string">&quot;basePackageClasses&quot;</span>)) &#123;</span><br><span class="line">			packageNames.add(basePackageClass.getPackage().getName());</span><br><span class="line">		&#125;</span><br><span class="line">           <span class="comment">//这里的packageNames就是空的，会走到这个if分支</span></span><br><span class="line">		<span class="keyword">if</span> (packageNames.isEmpty()) &#123;</span><br><span class="line">               <span class="comment">//packageNames增加当前SpringbootMybatisApplication类所在的包名com.example.springbootmybatis</span></span><br><span class="line">			packageNames.add(ClassUtils.getPackageName(metadata.getClassName()));</span><br><span class="line">		&#125;</span><br><span class="line">           <span class="comment">//这里的this.packageNames中就只会有com.example.springbootmybatis</span></span><br><span class="line">		<span class="built_in">this</span>.packageNames = Collections.unmodifiableList(packageNames);</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>

<p>我们再回头看上面的registerBeanDefinitions</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Registrar</span> <span class="keyword">implements</span> <span class="title class_">ImportBeanDefinitionRegistrar</span>, DeterminableImports &#123;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">registerBeanDefinitions</span><span class="params">(AnnotationMetadata metadata, BeanDefinitionRegistry registry)</span> &#123;</span><br><span class="line">       	<span class="comment">//上面new PackageImports(metadata)已经分析过了，这时new PackageImports(metadata).getPackageNames().toArray(new String[0])就是&#123;“com.example.springbootmybatis”&#125;</span></span><br><span class="line">        <span class="comment">//这个方法就不点进去了，在这里简单说说</span></span><br><span class="line">        <span class="comment">//方法就是在registry(也就是beanFatory)中增加一个bean的定义(BasePackagesBeanDefinition，它的参数就是&#123;“com.example.springbootmybatis”&#125;),所以上面的AutoConfigurationPackages.get(this.beanFactory)这句返回的结果就是&#123;“com.example.springbootmybatis”&#125;</span></span><br><span class="line">		register(registry, <span class="keyword">new</span> <span class="title class_">PackageImports</span>(metadata).getPackageNames().toArray(<span class="keyword">new</span> <span class="title class_">String</span>[<span class="number">0</span>]));</span><br><span class="line">	&#125;</span><br><span class="line">......</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<hr>
<p>我们继续看 public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) 中在registry(也就是beanFacotry)中增加的bean的定义registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition())</p>
<p>由于MapperScannerConfigurer这个类实现了BeanDefinitionRegistryPostProcessor,所以它就会被生成bean之前加载，调用它的postProcessBeanDefinitionRegistry方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br></pre></td><td class="code"><pre><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">postProcessBeanDefinitionRegistry</span><span class="params">(BeanDefinitionRegistry registry)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">this</span>.processPropertyPlaceHolders) &#123;</span><br><span class="line">      <span class="comment">//这个是主要设置一些属性，比如上面包名，要扫描的注解类名称等等</span></span><br><span class="line">      processPropertyPlaceHolders();</span><br><span class="line">    &#125;</span><br><span class="line">	<span class="comment">//这个类看名字，大家都知道是干什么的了。主要就是扫描mapper注解的类</span></span><br><span class="line">    <span class="type">ClassPathMapperScanner</span> <span class="variable">scanner</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathMapperScanner</span>(registry);</span><br><span class="line">    scanner.setAddToConfig(<span class="built_in">this</span>.addToConfig);</span><br><span class="line">    scanner.setAnnotationClass(<span class="built_in">this</span>.annotationClass);</span><br><span class="line">    scanner.setMarkerInterface(<span class="built_in">this</span>.markerInterface);</span><br><span class="line">    scanner.setSqlSessionFactory(<span class="built_in">this</span>.sqlSessionFactory);</span><br><span class="line">    scanner.setSqlSessionTemplate(<span class="built_in">this</span>.sqlSessionTemplate);</span><br><span class="line">    scanner.setSqlSessionFactoryBeanName(<span class="built_in">this</span>.sqlSessionFactoryBeanName);</span><br><span class="line">    scanner.setSqlSessionTemplateBeanName(<span class="built_in">this</span>.sqlSessionTemplateBeanName);</span><br><span class="line">    scanner.setResourceLoader(<span class="built_in">this</span>.applicationContext);</span><br><span class="line">    scanner.setBeanNameGenerator(<span class="built_in">this</span>.nameGenerator);</span><br><span class="line">    scanner.setMapperFactoryBeanClass(<span class="built_in">this</span>.mapperFactoryBeanClass);</span><br><span class="line">    <span class="keyword">if</span> (StringUtils.hasText(lazyInitialization)) &#123;</span><br><span class="line">      scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (StringUtils.hasText(defaultScope)) &#123;</span><br><span class="line">      scanner.setDefaultScope(defaultScope);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//这里是设置要扫描的注解类，这里会设置@Mapper</span></span><br><span class="line">    scanner.registerFilters();</span><br><span class="line">    <span class="comment">//这里就是要根据传入的包名去做扫描了，这里的this.basePackage就是上面说的com.example.springbootmybatis</span></span><br><span class="line">    scanner.scan(</span><br><span class="line">        StringUtils.tokenizeToStringArray(<span class="built_in">this</span>.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));</span><br><span class="line">  &#125;</span><br><span class="line">	<span class="keyword">public</span> <span class="type">int</span> <span class="title function_">scan</span><span class="params">(String... basePackages)</span> &#123;</span><br><span class="line">        ......</span><br><span class="line">		<span class="comment">//在这里进行mapper的扫描</span></span><br><span class="line">		doScan(basePackages);</span><br><span class="line">		......</span><br><span class="line">	&#125;</span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> Set&lt;BeanDefinitionHolder&gt; <span class="title function_">doScan</span><span class="params">(String... basePackages)</span> &#123;</span><br><span class="line">    <span class="comment">//首先会进入这里，我们进去看看  </span></span><br><span class="line">  Set&lt;BeanDefinitionHolder&gt; beanDefinitions = <span class="built_in">super</span>.doScan(basePackages);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (beanDefinitions.isEmpty()) &#123;</span><br><span class="line">    LOGGER.warn(() -&gt; <span class="string">&quot;No MyBatis mapper was found in &#x27;&quot;</span> + Arrays.toString(basePackages)</span><br><span class="line">        + <span class="string">&quot;&#x27; package. Please check your configuration.&quot;</span>);</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">     <span class="comment">//我们会走到这里，这个方法也比较重要，我们进去看看</span></span><br><span class="line">    processBeanDefinitions(beanDefinitions);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> beanDefinitions;</span><br><span class="line">&#125;</span><br><span class="line">	<span class="keyword">protected</span> Set&lt;BeanDefinitionHolder&gt; <span class="title function_">doScan</span><span class="params">(String... basePackages)</span> &#123;</span><br><span class="line">		Assert.notEmpty(basePackages, <span class="string">&quot;At least one base package must be specified&quot;</span>);</span><br><span class="line">		Set&lt;BeanDefinitionHolder&gt; beanDefinitions = <span class="keyword">new</span> <span class="title class_">LinkedHashSet</span>&lt;&gt;();</span><br><span class="line">        <span class="comment">//根据传入的包名遍历</span></span><br><span class="line">		<span class="keyword">for</span> (String basePackage : basePackages) &#123;</span><br><span class="line">            <span class="comment">//这里就是扫描类路径下的mapper注解类了。</span></span><br><span class="line">            <span class="comment">//比如我这里的传入的包名是com.example.springbootmybatis,就会被转换成classpath*:com/example/springbootmybatis/**/*.class这个路径进行解析查找，将找到的类作为BeanDefinition的定义，返回</span></span><br><span class="line">			Set&lt;BeanDefinition&gt; candidates = findCandidateComponents(basePackage);</span><br><span class="line">			<span class="keyword">for</span> (BeanDefinition candidate : candidates) &#123;</span><br><span class="line">				<span class="type">ScopeMetadata</span> <span class="variable">scopeMetadata</span> <span class="operator">=</span> <span class="built_in">this</span>.scopeMetadataResolver.resolveScopeMetadata(candidate);</span><br><span class="line">				candidate.setScope(scopeMetadata.getScopeName());</span><br><span class="line">                <span class="comment">//获取bean的名字</span></span><br><span class="line">				<span class="type">String</span> <span class="variable">beanName</span> <span class="operator">=</span> <span class="built_in">this</span>.beanNameGenerator.generateBeanName(candidate, <span class="built_in">this</span>.registry);</span><br><span class="line">                <span class="comment">//这里candidate的类型是ScannedGenericBeanDefinition,所以会进入这个if分支，这个没啥，就是设置一些bean初始化相关属性，不关注了</span></span><br><span class="line">				<span class="keyword">if</span> (candidate <span class="keyword">instanceof</span> AbstractBeanDefinition) &#123;</span><br><span class="line">					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);</span><br><span class="line">				&#125;</span><br><span class="line">                <span class="comment">//也会进入这个if分支，这个也不进去看了</span></span><br><span class="line">				<span class="keyword">if</span> (candidate <span class="keyword">instanceof</span> AnnotatedBeanDefinition) &#123;</span><br><span class="line">					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);</span><br><span class="line">				&#125;</span><br><span class="line">                <span class="comment">//这个是判断beanFactory是否包含beanName的bean的定义，不包含就会进入分支，这个分支也没啥特殊的，就是把bean的定义添加到beanFactory中</span></span><br><span class="line">				<span class="keyword">if</span> (checkCandidate(beanName, candidate)) &#123;</span><br><span class="line">					<span class="type">BeanDefinitionHolder</span> <span class="variable">definitionHolder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BeanDefinitionHolder</span>(candidate, beanName);</span><br><span class="line">					definitionHolder =</span><br><span class="line">							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, <span class="built_in">this</span>.registry);</span><br><span class="line">					beanDefinitions.add(definitionHolder);</span><br><span class="line">					registerBeanDefinition(definitionHolder, <span class="built_in">this</span>.registry);</span><br><span class="line">				&#125;</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">return</span> beanDefinitions;</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>

<p>继续会进入这个方法,这个方法比较长，不过比较重要，大家一起跟我看吧，非关键代码我都省略掉吧</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">processBeanDefinitions</span><span class="params">(Set&lt;BeanDefinitionHolder&gt; beanDefinitions)</span> &#123;</span><br><span class="line">	......</span><br><span class="line">	<span class="comment">//这里是给bean定义的添加一个构造方法参数，就是我们扫描出来mapper注解类的类名，我这里是com.example.springbootmybatis.mapper.UserMapper。这个是为后续选择哪个构造方法服务的</span></span><br><span class="line">    definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); <span class="comment">// issue #59</span></span><br><span class="line">    <span class="comment">//这个就是设置对应bean的类的类，这里设置成了org.mybatis.spring.mapper.MapperFactoryBean这个类，这注意这个类实现了FactoryBean接口</span></span><br><span class="line">    definition.setBeanClass(<span class="built_in">this</span>.mapperFactoryBeanClass);</span><br><span class="line">	......</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!explicitFactoryUsed) &#123;</span><br><span class="line">      LOGGER.debug(() -&gt; <span class="string">&quot;Enabling autowire by type for MapperFactoryBean with name &#x27;&quot;</span> + holder.getBeanName() + <span class="string">&quot;&#x27;.&quot;</span>);</span><br><span class="line">       <span class="comment">//这句也比较重要，代表属性注入模式</span></span><br><span class="line">      definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);</span><br><span class="line">    &#125;</span><br><span class="line">	......</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>到这里，对于@Mapper类的加载就完成了，后面的都是在生成对应bean的时候完成的</p>
<h3 id="4-beanFatory生成对应-Mapper类的bean对象"><a href="#4-beanFatory生成对应-Mapper类的bean对象" class="headerlink" title="4.beanFatory生成对应@Mapper类的bean对象"></a>4.beanFatory生成对应@Mapper类的bean对象</h3><p>创建bean对象实例会调用到AbstractBeanFactory的doGetBean这个方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">protected</span> &lt;T&gt; T <span class="title function_">doGetBean</span><span class="params">(</span></span><br><span class="line"><span class="params">			String name, <span class="meta">@Nullable</span> Class&lt;T&gt; requiredType, <span class="meta">@Nullable</span> Object[] args, <span class="type">boolean</span> typeCheckOnly)</span></span><br><span class="line">			<span class="keyword">throws</span> BeansException &#123;</span><br><span class="line">				......</span><br><span class="line">				<span class="comment">// Create bean instance.</span></span><br><span class="line">                <span class="comment">//由于我们的是单例对象，会走到这个分支</span></span><br><span class="line">				<span class="keyword">if</span> (mbd.isSingleton()) &#123;</span><br><span class="line">					sharedInstance = getSingleton(beanName, () -&gt; &#123;</span><br><span class="line">						<span class="keyword">try</span> &#123;</span><br><span class="line">                            <span class="comment">//在这个方法中会创建bean对象，我们下面看看这个方法</span></span><br><span class="line">							<span class="keyword">return</span> createBean(beanName, mbd, args);</span><br><span class="line">						&#125;</span><br><span class="line">						<span class="keyword">catch</span> (BeansException ex) &#123;</span><br><span class="line">							<span class="comment">// Explicitly remove instance from singleton cache: It might have been put there</span></span><br><span class="line">							<span class="comment">// eagerly by the creation process, to allow for circular reference resolution.</span></span><br><span class="line">							<span class="comment">// Also remove any beans that received a temporary reference to the bean.</span></span><br><span class="line">							destroySingleton(beanName);</span><br><span class="line">							<span class="keyword">throw</span> ex;</span><br><span class="line">						&#125;</span><br><span class="line">					&#125;);</span><br><span class="line">                    <span class="comment">//由于我们的sharedInstance对象是，所以在这里最终会调用到FactoryBeanRegistrySupport的doGetObjectFromFactoryBean方法，返回真正的userMapper的bean对象，也就是调用MapperFactoryBean的getObject()方法</span></span><br><span class="line">					beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);</span><br><span class="line">				&#125;</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>

<p>创建bean对象实例最终都会走到AbstractAutowireCapableBeanFactory类的doCreateBean这个方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line">	<span class="comment">//我们来分析下这块代码，不相关的代码我都省略掉</span></span><br><span class="line"></span><br><span class="line">		<span class="keyword">protected</span> Object <span class="title function_">doCreateBean</span><span class="params">(String beanName, RootBeanDefinition mbd, <span class="meta">@Nullable</span> Object[] args)</span></span><br><span class="line">			<span class="keyword">throws</span> BeanCreationException &#123;</span><br><span class="line"></span><br><span class="line">		<span class="comment">// Instantiate the bean.</span></span><br><span class="line">		<span class="type">BeanWrapper</span> <span class="variable">instanceWrapper</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">		<span class="keyword">if</span> (mbd.isSingleton()) &#123;</span><br><span class="line">            <span class="comment">//看this.factoryBeanInstanceCache这个名字就知道是factoryBean实例的缓存，其实我们当前的userMapper创建的实例已经缓存到这里了，不过无所谓，就算之前没有创建缓存到这里，下面12行就会去创建。所以我们这里就认为之前没有创建过，去看看13行的代码具体是如何创建userMapper的factoryBean实例的</span></span><br><span class="line">			instanceWrapper = <span class="built_in">this</span>.factoryBeanInstanceCache.remove(beanName);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">if</span> (instanceWrapper == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="comment">//正常创建userMapper的factoryBean对象是走不到这里的，是在这之前创建的，不过创建方法也是调用的这个</span></span><br><span class="line">            <span class="comment">//这个方法的所用就是根据RootBeanDefinition的getBeanClass()找到对应的类，再查找所有构造方法，根据 RootBeanDefinition.getConstructorArgumentValues()构造方法的参数选择合适的构造方法创建类对象，返回BeanWrapperImpl包装的对象</span></span><br><span class="line">            <span class="comment">//上面的processBeanDefinitions的方法中对RootBeanDefinition的beanClass和constructorArgumentValues都做过了专门的设置。</span></span><br><span class="line">            <span class="comment">//所以我们这里其实是调用的MapperFactoryBean(Class&lt;T&gt; mapperInterface)这个构造方法，里面的参数mapperInterface就是我们的mapper类com.example.springbootmybatis.mapper.UserMapper</span></span><br><span class="line">			instanceWrapper = createBeanInstance(beanName, mbd, args);</span><br><span class="line">		&#125;</span><br><span class="line">        <span class="comment">//这个就是获取创建出来的MapperFactoryBean对象</span></span><br><span class="line">		<span class="type">Object</span> <span class="variable">bean</span> <span class="operator">=</span> instanceWrapper.getWrappedInstance();</span><br><span class="line">        <span class="comment">//这个是创建创建出来的对象的类型，也就是org.mybatis.spring.mapper.MapperFactoryBean</span></span><br><span class="line">		Class&lt;?&gt; beanType = instanceWrapper.getWrappedClass();</span><br><span class="line">		<span class="keyword">if</span> (beanType != NullBean.class) &#123;</span><br><span class="line">			mbd.resolvedTargetType = beanType;</span><br><span class="line">		&#125;</span><br><span class="line">		......</span><br><span class="line"></span><br><span class="line">		<span class="comment">// Initialize the bean instance.</span></span><br><span class="line">		<span class="type">Object</span> <span class="variable">exposedObject</span> <span class="operator">=</span> bean;</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">//这里就是属性填充了，我们去这里看看</span></span><br><span class="line">			populateBean(beanName, mbd, instanceWrapper);</span><br><span class="line">            <span class="comment">//由于我们的MapperFactoryBean继承了SqlSessionDaoSupport,它继承了DaoSupport,它实现了InitializingBean这个接口，所以在这里也会调用到DaoSupport的afterPropertiesSet方法</span></span><br><span class="line">            exposedObject = initializeBean(beanName, exposedObject, mbd);</span><br><span class="line">        &#125;</span><br><span class="line">		<span class="keyword">return</span> exposedObject;</span><br><span class="line">	&#125;</span><br><span class="line"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">populateBean</span><span class="params">(String beanName, RootBeanDefinition mbd, <span class="meta">@Nullable</span> BeanWrapper bw)</span> &#123;</span><br><span class="line">	   ......</span><br><span class="line">           <span class="comment">//这个是获取RootBeanDefinition属性注入模式，我们的是在上面processBeanDefinitions这个方法中设置过的</span></span><br><span class="line">           <span class="comment">//definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);就是这句</span></span><br><span class="line">    	<span class="type">int</span> <span class="variable">resolvedAutowireMode</span> <span class="operator">=</span> mbd.getResolvedAutowireMode();</span><br><span class="line">		<span class="keyword">if</span> (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) &#123;</span><br><span class="line">			......</span><br><span class="line">			<span class="keyword">if</span> (resolvedAutowireMode == AUTOWIRE_BY_TYPE) &#123;</span><br><span class="line">                <span class="comment">//最终会走到这里，遍历类的所有属性，使用unsatisfiedNonSimpleProperties方法进行过滤，对属性进行注入。我们这里会对sqlSessionFactory，sqlSessionTemplate这两个属性进行注入(这两个注入的属性都是在MybatisAutoConfiguration的类中，通过方法定义的bean对象，上面也说过了)</span></span><br><span class="line">				autowireByType(beanName, mbd, bw, newPvs);</span><br><span class="line">			&#125;</span><br><span class="line">			pvs = newPvs;</span><br><span class="line">		&#125;</span><br><span class="line">		......</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>

<p>DaoSupport的afterPropertiesSet方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title function_">afterPropertiesSet</span><span class="params">()</span> <span class="keyword">throws</span> IllegalArgumentException, BeanInitializationException &#123;</span><br><span class="line">	<span class="comment">// Let abstract subclasses check their configuration.</span></span><br><span class="line">       <span class="comment">//这个方法是由子类MapperFactoryBean实现的，我们进去看看</span></span><br><span class="line">	checkDaoConfig();</span><br><span class="line"></span><br><span class="line">	<span class="comment">// Let concrete implementations initialize themselves.</span></span><br><span class="line">	<span class="keyword">try</span> &#123;</span><br><span class="line">		initDao();</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">		<span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">BeanInitializationException</span>(<span class="string">&quot;Initialization of DAO failed&quot;</span>, ex);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>MapperFactoryBean的checkDaoConfig方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"> <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">checkDaoConfig</span><span class="params">()</span> &#123;</span><br><span class="line">......</span><br><span class="line"><span class="comment">//这个是获取之前注入的SqlSessionTemplate的Configuration</span></span><br><span class="line">   <span class="type">Configuration</span> <span class="variable">configuration</span> <span class="operator">=</span> getSqlSession().getConfiguration();</span><br><span class="line">   <span class="keyword">if</span> (<span class="built_in">this</span>.addToConfig &amp;&amp; !configuration.hasMapper(<span class="built_in">this</span>.mapperInterface)) &#123;</span><br><span class="line">     <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">//会在这里添加我们的mapper类，这里的this.mapperInterface就是com.example.springbootmybatis.mapper.UserMapper，我们进到这里去看看</span></span><br><span class="line">       configuration.addMapper(<span class="built_in">this</span>.mapperInterface);</span><br><span class="line">     &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">       logger.error(<span class="string">&quot;Error while adding the mapper &#x27;&quot;</span> + <span class="built_in">this</span>.mapperInterface + <span class="string">&quot;&#x27; to configuration.&quot;</span>, e);</span><br><span class="line">       <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(e);</span><br><span class="line">     &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">       ErrorContext.instance().reset();</span><br><span class="line">     &#125;</span><br><span class="line">   &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>

<p>Configuration的addMapper方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> &lt;T&gt; T <span class="title function_">getMapper</span><span class="params">(Class&lt;T&gt; type, SqlSession sqlSession)</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> mapperRegistry.getMapper(type, sqlSession);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>MapperRegistry的addMapper方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> &lt;T&gt; <span class="keyword">void</span> <span class="title function_">addMapper</span><span class="params">(Class&lt;T&gt; type)</span> &#123;</span><br><span class="line">......</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="comment">//在knownMappers中添加一个key=com.example.springbootmybatis.mapper.UserMapper的MapperProxyFactory对象</span></span><br><span class="line">      knownMappers.put(type, <span class="keyword">new</span> <span class="title class_">MapperProxyFactory</span>&lt;&gt;(type));</span><br><span class="line">      <span class="comment">// It&#x27;s important that the type is added before the parser is run</span></span><br><span class="line">      <span class="comment">// otherwise the binding may automatically be attempted by the</span></span><br><span class="line">      <span class="comment">// mapper parser. If the type is already known, it won&#x27;t try.</span></span><br><span class="line">      <span class="comment">//在这里就是去查找mapper.xml文件了，同样的如果我们不是通过xml配置的sql，而是用注解的方式实现的，具体的查找都是通过下面的parse方法来实现，我们进去parse方法看看</span></span><br><span class="line">      <span class="type">MapperAnnotationBuilder</span> <span class="variable">parser</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MapperAnnotationBuilder</span>(config, type);</span><br><span class="line">      parser.parse();</span><br><span class="line">      loadCompleted = <span class="literal">true</span>;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (!loadCompleted) &#123;</span><br><span class="line">        knownMappers.remove(type);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>MapperAnnotationBuilder的parse方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">parse</span><span class="params">()</span> &#123;</span><br><span class="line">  <span class="type">String</span> <span class="variable">resource</span> <span class="operator">=</span> type.toString();</span><br><span class="line">  <span class="keyword">if</span> (!configuration.isResourceLoaded(resource)) &#123;</span><br><span class="line">    <span class="comment">//这行就是去从类路径加载mapper的xml文件了，具体的路径规则是这样的type.getName().replace(&#x27;.&#x27;, &#x27;/&#x27;) + &quot;.xml&quot;。所以如果我们的mapper的xml文件是按照这种规则指定的，就不需要单独通过mybatis.mapper-locations去单独指定mapper.xml的路径了</span></span><br><span class="line">    loadXmlResource();</span><br><span class="line">    configuration.addLoadedResource(resource);</span><br><span class="line">    assistant.setCurrentNamespace(type.getName());</span><br><span class="line">    parseCache();</span><br><span class="line">    parseCacheRef();</span><br><span class="line">    <span class="comment">//下面这块就是去扫描方法上的注解去生成sql配置了，这里就不进去看了</span></span><br><span class="line">    <span class="keyword">for</span> (Method method : type.getMethods()) &#123;</span><br><span class="line">      <span class="keyword">if</span> (!canHaveStatement(method)) &#123;</span><br><span class="line">        <span class="keyword">continue</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> (getAnnotationWrapper(method, <span class="literal">false</span>, Select.class, SelectProvider.class).isPresent()</span><br><span class="line">          &amp;&amp; method.getAnnotation(ResultMap.class) == <span class="literal">null</span>) &#123;</span><br><span class="line">        parseResultMap(method);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">try</span> &#123;</span><br><span class="line">        parseStatement(method);</span><br><span class="line">      &#125; <span class="keyword">catch</span> (IncompleteElementException e) &#123;</span><br><span class="line">        configuration.addIncompleteMethod(<span class="keyword">new</span> <span class="title class_">MethodResolver</span>(<span class="built_in">this</span>, method));</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  parsePendingMethods();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>我们再回到doGetBean方法看后面的</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">protected</span> &lt;T&gt; T <span class="title function_">doGetBean</span><span class="params">(</span></span><br><span class="line"><span class="params">			String name, <span class="meta">@Nullable</span> Class&lt;T&gt; requiredType, <span class="meta">@Nullable</span> Object[] args, <span class="type">boolean</span> typeCheckOnly)</span></span><br><span class="line">			<span class="keyword">throws</span> BeansException &#123;</span><br><span class="line">					......</span><br><span class="line">                    <span class="comment">//由于我们的sharedInstance对象是，所以在这里最终会调用到FactoryBeanRegistrySupport的doGetObjectFromFactoryBean方法，返回真正的userMapper的bean对象，也就是调用MapperFactoryBean的getObject()方法</span></span><br><span class="line">					beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);</span><br><span class="line">				&#125;</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>

<p>MapperFactoryBean的getObject方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> T <span class="title function_">getObject</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"><span class="comment">//这个getSqlSession()就是我们上面属性注入的。org.mybatis.spring.SqlSessionTemplate的对象，</span></span><br><span class="line">  <span class="keyword">return</span> getSqlSession().getMapper(<span class="built_in">this</span>.mapperInterface);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>最终会调用到MapperRegistry的getMapper方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> &lt;T&gt; T <span class="title function_">getMapper</span><span class="params">(Class&lt;T&gt; type, SqlSession sqlSession)</span> &#123;</span><br><span class="line">   <span class="comment">// 我们在上面addMapper的方法中讲过knownMappers已经添加了key=com.example.springbootmybatis.mapper.UserMapper的MapperProxyFactory对象，</span></span><br><span class="line">   <span class="keyword">final</span> MapperProxyFactory&lt;T&gt; mapperProxyFactory = (MapperProxyFactory&lt;T&gt;) knownMappers.get(type);</span><br><span class="line">   <span class="keyword">if</span> (mapperProxyFactory == <span class="literal">null</span>) &#123;</span><br><span class="line">     <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">BindingException</span>(<span class="string">&quot;Type &quot;</span> + type + <span class="string">&quot; is not known to the MapperRegistry.&quot;</span>);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">try</span> &#123;</span><br><span class="line">     <span class="comment">//这里就是调用MapperProxyFactory的newInstance的方法了</span></span><br><span class="line">     <span class="keyword">return</span> mapperProxyFactory.newInstance(sqlSession);</span><br><span class="line">   &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">     <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">BindingException</span>(<span class="string">&quot;Error getting mapper instance. Cause: &quot;</span> + e, e);</span><br><span class="line">   &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>

<p>MapperProxyFactory的newInstance方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> T <span class="title function_">newInstance</span><span class="params">(SqlSession sqlSession)</span> &#123;</span><br><span class="line">  <span class="keyword">final</span> MapperProxy&lt;T&gt; mapperProxy = <span class="keyword">new</span> <span class="title class_">MapperProxy</span>&lt;&gt;(sqlSession, mapperInterface, methodCache);</span><br><span class="line">  <span class="keyword">return</span> newInstance(mapperProxy);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">protected</span> T <span class="title function_">newInstance</span><span class="params">(MapperProxy&lt;T&gt; mapperProxy)</span> &#123;</span><br><span class="line">  <span class="comment">//最终调用到这里，创建一个MapperProxy的代理对象，这个也就是真正的创建的bean对象</span></span><br><span class="line">  <span class="keyword">return</span> (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), <span class="keyword">new</span> <span class="title class_">Class</span>[] &#123; mapperInterface &#125;, mapperProxy);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>到这里，springboot整合mybatis到创建出来mapper对象，整个流程就到这里了，后面调用mapper的方法其实也就是通过MapperProxy代理来实现的。具体springboot中调用mybatis的执行的流程将在接下来的一篇给大家讲解。</p>
<h3 id="5-关于-MapperScan"><a href="#5-关于-MapperScan" class="headerlink" title="5.关于@MapperScan"></a>5.关于@MapperScan</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@MapperScan(<span class="params">basePackages = <span class="string">&quot;com.example.springbootmybatis&quot;</span>,annotationClass = Mapper.<span class="keyword">class</span></span>)</span></span><br></pre></td></tr></table></figure>

<blockquote>
<p>basePackages是指定扫描的包名</p>
<p>annotationClass是查找对应包名下类上有个Mapper注解的接口，如果没有指定这个参数，会将获取到的所有的类都当作Mapper去处理，这时后面执行sql操作就会出错了<strong>不知道是不是mybatis的版本问题，之前记得是不需要指定这个参数的</strong></p>
<p>具体的代码位置是在ClassPathMapperScanner类的registerFilters方法中</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">registerFilters</span><span class="params">()</span> &#123;</span><br><span class="line">   <span class="type">boolean</span> <span class="variable">acceptAllInterfaces</span> <span class="operator">=</span> <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line">   <span class="comment">// if specified, use the given annotation and / or marker interface</span></span><br><span class="line">   <span class="comment">//如果设置了annotationClass = Mapper.class就会走到这里，在这里会过滤class上是否有Mapper注解</span></span><br><span class="line">   <span class="keyword">if</span> (<span class="built_in">this</span>.annotationClass != <span class="literal">null</span>) &#123;</span><br><span class="line">     addIncludeFilter(<span class="keyword">new</span> <span class="title class_">AnnotationTypeFilter</span>(<span class="built_in">this</span>.annotationClass));</span><br><span class="line">     acceptAllInterfaces = <span class="literal">false</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">// override AssignableTypeFilter to ignore matches on the actual marker interface</span></span><br><span class="line">   <span class="keyword">if</span> (<span class="built_in">this</span>.markerInterface != <span class="literal">null</span>) &#123;</span><br><span class="line">     addIncludeFilter(<span class="keyword">new</span> <span class="title class_">AssignableTypeFilter</span>(<span class="built_in">this</span>.markerInterface) &#123;</span><br><span class="line">       <span class="meta">@Override</span></span><br><span class="line">       <span class="keyword">protected</span> <span class="type">boolean</span> <span class="title function_">matchClassName</span><span class="params">(String className)</span> &#123;</span><br><span class="line">         <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">       &#125;</span><br><span class="line">     &#125;);</span><br><span class="line">     acceptAllInterfaces = <span class="literal">false</span>;</span><br><span class="line">   &#125;</span><br><span class="line">	</span><br><span class="line">   <span class="comment">//没有设置了annotationClass = Mapper.class，就会走这里，直接返回true</span></span><br><span class="line">   <span class="keyword">if</span> (acceptAllInterfaces) &#123;</span><br><span class="line">     <span class="comment">// default include filter that accepts all classes</span></span><br><span class="line">     addIncludeFilter((metadataReader, metadataReaderFactory) -&gt; <span class="literal">true</span>);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">// exclude package-info.java</span></span><br><span class="line">   addExcludeFilter((metadataReader, metadataReaderFactory) -&gt; &#123;</span><br><span class="line">     <span class="type">String</span> <span class="variable">className</span> <span class="operator">=</span> metadataReader.getClassMetadata().getClassName();</span><br><span class="line">     <span class="keyword">return</span> className.endsWith(<span class="string">&quot;package-info&quot;</span>);</span><br><span class="line">   &#125;);</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>
</blockquote>
<p>大家看到我的demo中是没有使用@MapperScan这个注解的，那什么时候使用这个注解呢，下面我们从源码来看看</p>
<p>MapperScan注解上面会有@Import(MapperScannerRegistrar.class)，@Repeatable(MapperScans.class)这两个注解，MapperScannerRegistrar这个注解实现了ImportBeanDefinitionRegistrar，加载主类的过程中会调用registerBeanDefinitions这个方法，</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">registerBeanDefinitions</span><span class="params">(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)</span> &#123;</span><br><span class="line">  <span class="comment">//这个是获取主类上MapperScan注解的相关属性比如我们的配置是(@MapperScan(basePackages = &quot;com.example.springbootmybatis&quot;))，比如basePackages属性等等都是这个注解上的</span></span><br><span class="line">  <span class="type">AnnotationAttributes</span> <span class="variable">mapperScanAttrs</span> <span class="operator">=</span> AnnotationAttributes</span><br><span class="line">      .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));</span><br><span class="line">  <span class="keyword">if</span> (mapperScanAttrs != <span class="literal">null</span>) &#123;</span><br><span class="line">    <span class="comment">//这里就会根据这些属性创建一个MapperScannerConfigurer类的bean的定义，添加到beanFatory中</span></span><br><span class="line">    registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,</span><br><span class="line">        generateBaseBeanName(importingClassMetadata, <span class="number">0</span>));</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>我们再看看之前说的MybatisAutoConfiguration.MapperScannerRegistrarNotFoundConfiguration这个内部类</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@org</span>.springframework.context.annotation.Configuration</span><br><span class="line"><span class="meta">@Import(AutoConfiguredMapperScannerRegistrar.class)</span></span><br><span class="line"><span class="comment">//由于我们上面已经在beanFactory中添加了MapperScannerConfigurer这个类型的bean的定义，所以这个条件就不会成立，上面的import注解中导入AutoConfiguredMapperScannerRegistrar类也就不会执行</span></span><br><span class="line"><span class="meta">@ConditionalOnMissingBean(&#123; MapperFactoryBean.class, MapperScannerConfigurer.class &#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">MapperScannerRegistrarNotFoundConfiguration</span> <span class="keyword">implements</span> <span class="title class_">InitializingBean</span> &#123;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Override</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterPropertiesSet</span><span class="params">()</span> &#123;</span><br><span class="line">    logger.debug(</span><br><span class="line">        <span class="string">&quot;Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.&quot;</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>所以@MapperScan的区别主要就在于MapperScannerConfigurer这个bean定义的生成位置不一样</p>
 
      <!-- reward -->
      
      <div id="reword-out">
        <div id="reward-btn">
          打赏
        </div>
      </div>
      
    </div>
    

    <!-- copyright -->
    
    <div class="declare">
      <ul class="post-copyright">
        <li>
          <i class="ri-copyright-line"></i>
          <strong>版权声明： </strong>
          
          本博客所有文章除特别声明外，著作权归作者所有。转载请注明出处！
          
        </li>
      </ul>
    </div>
    
    <footer class="article-footer">
       
<div class="share-btn">
      <span class="share-sns share-outer">
        <i class="ri-share-forward-line"></i>
        分享
      </span>
      <div class="share-wrap">
        <i class="arrow"></i>
        <div class="share-icons">
          
          <a class="weibo share-sns" href="javascript:;" data-type="weibo">
            <i class="ri-weibo-fill"></i>
          </a>
          <a class="weixin share-sns wxFab" href="javascript:;" data-type="weixin">
            <i class="ri-wechat-fill"></i>
          </a>
          <a class="qq share-sns" href="javascript:;" data-type="qq">
            <i class="ri-qq-fill"></i>
          </a>
          <a class="douban share-sns" href="javascript:;" data-type="douban">
            <i class="ri-douban-line"></i>
          </a>
          <!-- <a class="qzone share-sns" href="javascript:;" data-type="qzone">
            <i class="icon icon-qzone"></i>
          </a> -->
          
          <a class="facebook share-sns" href="javascript:;" data-type="facebook">
            <i class="ri-facebook-circle-fill"></i>
          </a>
          <a class="twitter share-sns" href="javascript:;" data-type="twitter">
            <i class="ri-twitter-fill"></i>
          </a>
          <a class="google share-sns" href="javascript:;" data-type="google">
            <i class="ri-google-fill"></i>
          </a>
        </div>
      </div>
</div>

<div class="wx-share-modal">
    <a class="modal-close" href="javascript:;"><i class="ri-close-circle-line"></i></a>
    <p>扫一扫，分享到微信</p>
    <div class="wx-qrcode">
      <img src="//api.qrserver.com/v1/create-qr-code/?size=150x150&data=https://blog.cydh.top/2023/07/06/springboot%E6%95%B4%E5%90%88mybatis%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/" alt="微信分享二维码">
    </div>
</div>

<div id="share-mask"></div>  
    </footer>
  </div>

   
  <nav class="article-nav">
    
      <a href="/2023/07/07/%E7%94%B5%E6%B1%A0%E7%AE%A1%E7%90%86%E7%B3%BB%E7%BB%9FBMS%E7%9F%A5%E8%AF%86%E4%B8%8E%E5%8A%9F%E8%83%BD%E5%85%A5%E9%97%A8%E7%AF%87/" class="article-nav-link">
        <strong class="article-nav-caption">上一篇</strong>
        <div class="article-nav-title">
          
            电池管理系统BMS知识与功能入门篇
          
        </div>
      </a>
    
    
      <a href="/2023/07/04/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E5%9F%BA%E7%A1%80/" class="article-nav-link">
        <strong class="article-nav-caption">下一篇</strong>
        <div class="article-nav-title">分布式事务基础（转载https://juejin.cn/post/6844903647197806605）</div>
      </a>
    
  </nav>

  
   
  
   
    <script src="https://cdn.staticfile.org/twikoo/1.4.18/twikoo.all.min.js"></script>
    <div id="twikoo" class="twikoo"></div>
    <script>
        twikoo.init({
            envId: ""
        })
    </script>
 
</article>

</section>
      <footer class="footer">
  <div class="outer">
    <ul>
      <li>
        Copyrights &copy;
        2015-2023
        <i class="ri-heart-fill heart_icon"></i> cyd
      </li>
    </ul>
    <ul>
      <li>
        
      </li>
    </ul>
    <ul>
      <li>
        
        
        <span>
  <span><i class="ri-user-3-fill"></i>访问人数:<span id="busuanzi_value_site_uv"></span></span>
  <span class="division">|</span>
  <span><i class="ri-eye-fill"></i>浏览次数:<span id="busuanzi_value_page_pv"></span></span>
</span>
        
      </li>
    </ul>
    <ul>
      
    </ul>
    <ul>
      
    </ul>
    <ul>
      <li>
        <!-- cnzz统计 -->
        
        <script type="text/javascript" src='https://s9.cnzz.com/z_stat.php?id=1278069914&amp;web_id=1278069914'></script>
        
      </li>
    </ul>
  </div>
</footer>    
    </main>
    <div class="float_btns">
      <div class="totop" id="totop">
  <i class="ri-arrow-up-line"></i>
</div>

<div class="todark" id="todark">
  <i class="ri-moon-line"></i>
</div>

    </div>
    <aside class="sidebar on">
      <button class="navbar-toggle"></button>
<nav class="navbar">
  
  <div class="logo">
    <a href="/"><img src="/images/ayer-side.svg" alt="风舞神者"></a>
  </div>
  
  <ul class="nav nav-main">
    
    <li class="nav-item">
      <a class="nav-item-link" href="/">主页</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/archives">归档</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/categories">分类</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags">标签</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/%E6%97%85%E8%A1%8C/">旅行</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" target="_blank" rel="noopener" href="http://shenyu-vip.lofter.com">摄影</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/friends">友链</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/2019/about">关于我</a>
    </li>
    
  </ul>
</nav>
<nav class="navbar navbar-bottom">
  <ul class="nav">
    <li class="nav-item">
      
      <a class="nav-item-link nav-item-search"  title="搜索">
        <i class="ri-search-line"></i>
      </a>
      
      
      <a class="nav-item-link" target="_blank" href="/atom.xml" title="RSS Feed">
        <i class="ri-rss-line"></i>
      </a>
      
    </li>
  </ul>
</nav>
<div class="search-form-wrap">
  <div class="local-search local-search-plugin">
  <input type="search" id="local-search-input" class="local-search-input" placeholder="Search...">
  <div id="local-search-result" class="local-search-result"></div>
</div>
</div>
    </aside>
    <div id="mask"></div>

<!-- #reward -->
<div id="reward">
  <span class="close"><i class="ri-close-line"></i></span>
  <p class="reward-p"><i class="ri-cup-line"></i>请我喝杯咖啡吧~</p>
  <div class="reward-box">
    
    <div class="reward-item">
      <img class="reward-img" src="/images/alipay.jpg">
      <span class="reward-type">支付宝</span>
    </div>
    
    
    <div class="reward-item">
      <img class="reward-img" src="/images/wechat.jpg">
      <span class="reward-type">微信</span>
    </div>
    
  </div>
</div>
    
<script src="/js/jquery-3.6.0.min.js"></script>
 
<script src="/js/lazyload.min.js"></script>

<!-- Tocbot -->
 
<script src="/js/tocbot.min.js"></script>

<script>
  tocbot.init({
    tocSelector: ".tocbot",
    contentSelector: ".article-entry",
    headingSelector: "h1, h2, h3, h4, h5, h6",
    hasInnerContainers: true,
    scrollSmooth: true,
    scrollContainer: "main",
    positionFixedSelector: ".tocbot",
    positionFixedClass: "is-position-fixed",
    fixedSidebarOffset: "auto",
  });
</script>

<script src="https://cdn.staticfile.org/jquery-modal/0.9.2/jquery.modal.min.js"></script>
<link
  rel="stylesheet"
  href="https://cdn.staticfile.org/jquery-modal/0.9.2/jquery.modal.min.css"
/>
<script src="https://cdn.staticfile.org/justifiedGallery/3.8.1/js/jquery.justifiedGallery.min.js"></script>

<script src="/dist/main.js"></script>

<!-- ImageViewer -->
 <!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                        <div class="pswp__preloader__cut">
                            <div class="pswp__preloader__donut"></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div>
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>

<link rel="stylesheet" href="https://cdn.staticfile.org/photoswipe/4.1.3/photoswipe.min.css">
<link rel="stylesheet" href="https://cdn.staticfile.org/photoswipe/4.1.3/default-skin/default-skin.min.css">
<script src="https://cdn.staticfile.org/photoswipe/4.1.3/photoswipe.min.js"></script>
<script src="https://cdn.staticfile.org/photoswipe/4.1.3/photoswipe-ui-default.min.js"></script>

<script>
    function viewer_init() {
        let pswpElement = document.querySelectorAll('.pswp')[0];
        let $imgArr = document.querySelectorAll(('.article-entry img:not(.reward-img)'))

        $imgArr.forEach(($em, i) => {
            $em.onclick = () => {
                // slider展开状态
                // todo: 这样不好，后面改成状态
                if (document.querySelector('.left-col.show')) return
                let items = []
                $imgArr.forEach(($em2, i2) => {
                    let img = $em2.getAttribute('data-idx', i2)
                    let src = $em2.getAttribute('data-target') || $em2.getAttribute('src')
                    let title = $em2.getAttribute('alt')
                    // 获得原图尺寸
                    const image = new Image()
                    image.src = src
                    items.push({
                        src: src,
                        w: image.width || $em2.width,
                        h: image.height || $em2.height,
                        title: title
                    })
                })
                var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, {
                    index: parseInt(i)
                });
                gallery.init()
            }
        })
    }
    viewer_init()
</script> 
<!-- MathJax -->

<!-- Katex -->

<!-- busuanzi  -->
 
<script src="/js/busuanzi-2.3.pure.min.js"></script>
 
<!-- ClickLove -->

<!-- ClickBoom1 -->

<!-- ClickBoom2 -->

<!-- CodeCopy -->
 
<link rel="stylesheet" href="/css/clipboard.css">
 <script src="https://cdn.staticfile.org/clipboard.js/2.0.10/clipboard.min.js"></script>
<script>
  function wait(callback, seconds) {
    var timelag = null;
    timelag = window.setTimeout(callback, seconds);
  }
  !function (e, t, a) {
    var initCopyCode = function(){
      var copyHtml = '';
      copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
      copyHtml += '<i class="ri-file-copy-2-line"></i><span>COPY</span>';
      copyHtml += '</button>';
      $(".highlight .code pre").before(copyHtml);
      $(".article pre code").before(copyHtml);
      var clipboard = new ClipboardJS('.btn-copy', {
        target: function(trigger) {
          return trigger.nextElementSibling;
        }
      });
      clipboard.on('success', function(e) {
        let $btn = $(e.trigger);
        $btn.addClass('copied');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-checkbox-circle-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPIED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-checkbox-circle-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
      clipboard.on('error', function(e) {
        e.clearSelection();
        let $btn = $(e.trigger);
        $btn.addClass('copy-failed');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-time-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPY FAILED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-time-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
    }
    initCopyCode();
  }(window, document);
</script>
 
<!-- CanvasBackground -->

<script>
  if (window.mermaid) {
    mermaid.initialize({ theme: "forest" });
  }
</script>


    
    

  </div>
</body>

</html>